home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume3 / nwho < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  23.6 KB

  1. From: genrad!decvax!linus!rayssd!gmp (Gregory M. Paris)
  2. Subject: nwho - enhanced "who" program
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 69
  7. Submitted by: genrad!decvax!linus!rayssd!gmp (Gregory M. Paris)
  8.  
  9.  
  10. The following shell archive is source and manual page for an enhanced
  11. "who" program, called "nwho".  This program expands aliases for login
  12. names, sorts alphabetically by name, and outputs in a space efficient
  13. multi-column format.  Binary size is nearly the same as the standard
  14. /bin/who, as is speed of execution.  It will compile and run on 2.9,
  15. 4.1, and 4.2 BSD UNIX systems.  It does not do "who am i" and it is
  16. not intended to be used on "wtmp" files (it is not meant to be a
  17. replacement for /bin/who).
  18.  
  19. ++---------------------------------------------------------------------------++
  20. ||  Greg Paris             {allegra,linus,raybed2,ccice5,brunix}!rayssd!gmp  ||
  21. ++---------------------------------------------------------------------------++
  22. ---------------------------------cut here--------------------------------------
  23. #! /bin/sh
  24. # This is a shell archive, meaning:
  25. # 1. Remove everything above the #! /bin/sh line.
  26. # 2. Save the resulting text in a file.
  27. # 3. Execute the file with /bin/sh (not csh) to create the files:
  28. #    README
  29. #    nwho.1
  30. #    nwho.c
  31. # This archive created: Tue Dec 24 09:43:29 1985
  32. export PATH; PATH=/bin:$PATH
  33. echo shar: extracting "'README'" '(1501 characters)'
  34. if test -f 'README'
  35. then
  36.     echo shar: will not over-write existing file "'README'"
  37. else
  38. cat << \SHAR_EOF > 'README'
  39. This is an enhanced who program.  Features:
  40.     1)  expands aliases for login names
  41.     2)  sorts output alphabetically by name
  42.     3)  space-efficient multi-column output
  43.     4)  speed comparable to /bin/who
  44.     5)  size comparable to /bin/who
  45.  
  46. To compile:
  47.     This program is used regularly on 2.9, 4.1 and 4.2 BSD UNIX
  48.     systems (PDP's, VAXen, SUN-2's, and Pyramid 90x's) here at rayssd.
  49.     (We used to have a V7 system that an earlier version worked on, so
  50.     I think that with "#define void int", it should work fine.)
  51.     If you're on a 2.9 or 4.2 system, #define bsd42.  Else, if you're
  52.     on a system that has a ut_host field in the utmp structure, you should
  53.     #define hashosts.  If your system has tty id's that are other than 2
  54.     letters long (the "xx" in "ttyxx"), you'll need to #define TTYLEN to
  55.     that length.  You'll need getopt() to compile this program.  If you
  56.     don't have termcap, you can take out the guts of termlen() or adapt
  57.     the program to use terminfo (send me the diffs!).
  58.  
  59.     A good example is a vanilla Pyramid 90x system running Berkley init.
  60.  
  61.         cc -O -Dbsd42 -DTTYLEN=3 -o nwho nwho.c getopt.o -ltermcap
  62.  
  63. Problems:
  64.     If you have any problems, I'd like to know about them.  If you get
  65.     this program running on any type of system not mentioned above, I'd be
  66.     interested in the diffs to make it work.  If you have any useful
  67.     enhancements, I'd like to know about them too.
  68.  
  69.         Greg Paris          { allegra, linus, raybed2 }!rayssd!gmp
  70.         PO Box 360/MS 330
  71.         Portsmouth, RI  02871
  72.         (401) 847-8000 ext. 3839
  73.  
  74. SHAR_EOF
  75. if test 1501 -ne "`wc -c < 'README'`"
  76. then
  77.     echo shar: error transmitting "'README'" '(should have been 1501 characters)'
  78. fi
  79. fi
  80. echo shar: extracting "'nwho.1'" '(3269 characters)'
  81. if test -f 'nwho.1'
  82. then
  83.     echo shar: will not over-write existing file "'nwho.1'"
  84. else
  85. cat << \SHAR_EOF > 'nwho.1'
  86. .TH NWHO 1  "Raytheon SSD SGF"
  87. .SH NAME
  88. nwho \- column sorted, aliased list of who's logged-on
  89. .SH SYNOPSIS
  90. .B nwho
  91. .B [ -qht ]
  92. .B [ -l length ]
  93. .B [ -c columns ]
  94. .B [ -a file ]
  95. .SH DESCRIPTION
  96. The
  97. .I nwho
  98. program is an updated version of the standard
  99. .IR who (1)
  100. program.
  101. Enhancements are multi-column formatting,
  102. login-alias expansion,
  103. and output in alphabetical order by name,
  104. rather than terminal order.
  105. The program's output format is more economical
  106. as it does not print the (usually redundant) date
  107. and "tty" strings of
  108. .IR who .
  109. .PP
  110. For each user logged-in,
  111. .I nwho
  112. produces an output entry with format
  113. .nf
  114. .in +4
  115. .sp 1
  116. .B tt hh:mm name
  117. .sp 1
  118. .in -4
  119. .fi
  120. where
  121. .I tt
  122. is the last two letters of the login tty
  123. ("co" if it's the console),
  124. and
  125. .IR hh : mm
  126. is the login time
  127. (24-hour format, no leading zero),
  128. and
  129. .I name
  130. is either the login name of the user,
  131. or an alias for that login name.
  132. By default, output is multi-column.
  133. The number of columns is the maximum which
  134. will allow equal-width columns,
  135. as wide as the widest output entry,
  136. to fit within the output line length (default 79 characters).
  137. If any output entries are longer than the line length,
  138. the output will be single-column, and those entries will
  139. be truncated.
  140. Sorting is done down columns
  141. rather than across rows.
  142. .PP
  143. Aliases for login names are specified for the
  144. .I nwho
  145. program in a ".nwhorc" file in the invoker's home directory
  146. (as named by the "HOME" environment variable).
  147. If the file exists,
  148. .I nwho
  149. will replace matched login names with their respective aliases.
  150. The file consists of one entry per line, each line
  151. consisting of a login name,
  152. followed by white space (spaces and tabs),
  153. followed by the alias for the login name.
  154. The alias is terminated by the end of line,
  155. therefore may contain embedded white space.
  156. The contents of the .nwhorc file
  157. .I must
  158. .I be
  159. .I sorted
  160. .I alphabetically.
  161. Alias expansion will not be done properly if the file is not sorted.
  162. .PP
  163. Options to
  164. .I nwho
  165. are as listed below.
  166. (This program uses
  167. .IR getopt(3)
  168. to parse its arguments.)
  169. .nf
  170. .in +4
  171. .sp 1
  172. .ta 1.6i
  173. -a file    use "file" for aliases instead of .nwhorc
  174. -c columns    use "columns" as maximum number of columns
  175. -h    output remote host field (if supported)
  176. -l length    use "length" as line length, or
  177. -l t    look up line length in termcap using $TERM, or
  178. -l t=term    look up line length in termcap using "term"
  179. -q    quick -- don't do alias expansion
  180. -t    output in terminal order (don't alphabetize)
  181. .sp 1
  182. .in -4
  183. .fi
  184. .SH "DIAGNOSTICS"
  185. If the line length is smaller than that needed to print
  186. the terminal id, the time, and one character of the name,
  187. .I nwho
  188. will complain then exit.
  189. .sp 1
  190. Other diagnostics are self-explanatory.
  191. .sp 1
  192. Exits with status 0 if all goes well,
  193. 1 on a user-generated error (e.g., bad option),
  194. and 2 on internal error.
  195. .SH "BUGS"
  196. Doesn't complain if .nwhorc is not sorted.
  197. .sp 1
  198. Avoids problems with premature line wrapping by using
  199. a default line length of 79 rather than 80.
  200. If the -lt option is used,
  201. .I nwho
  202. will use a line length of one less than that listed
  203. in termcap if the terminal wraps (automatic margins).
  204. This cheats many terminals out of one space per line.
  205. .SH "AUTHOR"
  206. Greg Paris, Raytheon Submarine Signal Div., Portsmouth, RI
  207. .SH "SEE ALSO"
  208. who(1), getopt(3), termcap(5), utmp(5)
  209. SHAR_EOF
  210. if test 3269 -ne "`wc -c < 'nwho.1'`"
  211. then
  212.     echo shar: error transmitting "'nwho.1'" '(should have been 3269 characters)'
  213. fi
  214. fi
  215. echo shar: extracting "'nwho.c'" '(17037 characters)'
  216. if test -f 'nwho.c'
  217. then
  218.     echo shar: will not over-write existing file "'nwho.c'"
  219. else
  220. cat << \SHAR_EOF > 'nwho.c'
  221. /* vi:set sw=4 ts=4: */
  222. #ifndef    lint
  223. static char       *sccsid = "@(#)nwho.c    3.12 rayssd!gmp 12/22/85";
  224. #endif    lint
  225. /*
  226. **    nwho.c (version 3.12): written by
  227. **        Greg Paris
  228. **        Raytheon Submarine Signal Division
  229. **        Portsmouth, RI  02871
  230. */
  231.  
  232. /*
  233. **    NOTE: correct operation of this program depends
  234. **        on the alias file being sorted by login name.
  235. **
  236. **    NOTE: the "-u utmp" option is undocumented.  This
  237. **        program is quite memory intensive -- great for
  238. **        /etc/utmp use, but a real hog when used on large
  239. **        /usr/adm/wtmp files.
  240. */
  241.  
  242. #include    <sys/types.h>
  243. #include    <utmp.h>
  244. #include    <sys/stat.h>
  245.  
  246. #ifdef    bsd42
  247. #define        hashosts
  248. #include    <sys/time.h>
  249. #else    bsd42
  250. #include    <time.h>
  251. #include    <sys/timeb.h>
  252. #endif    bsd42
  253.  
  254. #ifdef    pyr
  255. /*
  256. **    compile in ucb universe
  257. **    works with ucb init being run
  258. */
  259. char        utmpfl[]    = "/etc/.ucbutmp";        /* for att users */
  260. #else    pyr
  261. char        utmpfl[]    = "/etc/utmp";            /* default utmp file */
  262. #endif    pyr
  263.  
  264. char        aliasfl[]    = ".nwhorc";            /* default alias file */
  265.  
  266. char        *progname;                            /* program name */
  267. int            proglen;                            /* program name length */
  268. char        *requtmpfl;                            /* requested utmp file */
  269. char        *reqaliasfl;                        /* requested alias file */
  270.  
  271. extern int    optind;
  272. extern char    *optarg;
  273. extern int    getopt();
  274. #ifndef    EOF
  275. #define        EOF        -1
  276. #endif    EOF
  277.  
  278. int            logsort(), alsort(), termsort();
  279. char        *strcpy(), *strncpy(), *index(), *timefmt();
  280. char        *getenv(), *malloc(), *getaliases(), *logalias();
  281. long        gmtoffset();
  282.  
  283. /*
  284. **    plus: structure for aliasing login names
  285. */
  286. struct plus {
  287.     struct utmp        *pu;            /* pointer to utmp entry */
  288.     char            *al;            /* pointer to alias */
  289. };
  290.  
  291. struct utmp    dumutmp;                            /* for SZ defines */
  292.  
  293. #ifndef    TTYLEN
  294. #define    TTYLEN        2                            /* assume ttyxx */
  295. #endif    TTYLEN
  296. #define    MINENTLEN    (TTYLEN+7)                    /* space for term and time */
  297. #define    MINSPACES    2                            /* min between columns */
  298. #define    WHITESPACE    case ' ': case '\t'            /* white space in alias file */
  299. #define    DEFLINELEN    79                            /* avoids premature wrapping */
  300.  
  301. #define    SZ_UTMP        sizeof(struct utmp)
  302. #define    SZ_PLUS        sizeof(struct plus)
  303. #define    SZ_UTNAME    sizeof(dumutmp.ut_name)
  304.  
  305. #ifdef    hashosts
  306. #define    SZ_UTHOST    sizeof(dumutmp.ut_host)
  307. #endif    hashosts
  308.  
  309. /*
  310. **    add characters to output buffer
  311. */
  312. #define    bufadd(str)    for(fr = str;*fr;*pb++ = *fr++)
  313. #define    bufaddn(st, nn)    for(fr = st, i = nn;*fr && i--;*pb++ = *fr++)
  314.  
  315. /*
  316. **    add TTYLEN-letter abbreviation for terminal name
  317. **    loses if non-tty devices named t* exist
  318. */
  319. #define    buftty(tty)    if(tty[0] == 't') bufadd(tty+3); \
  320.         else bufaddn(tty, TTYLEN)
  321.  
  322. /*
  323. **    add " hh:mm "
  324. */
  325. #define    buftime(tm)    bufadd(timefmt(tm -= offset))
  326.  
  327. /*
  328. **    add alias or login name
  329. */
  330. #define    bufname(ppp)    \
  331.         if(ppp->al) bufadd(ppp->al); \
  332.         else bufaddn(ppp->pu->ut_name, SZ_UTNAME)
  333.  
  334. #ifdef    hashosts
  335. /*
  336. **    add " (hostname)"
  337. */
  338. #define    bufhost(ppp)    \
  339.         if(hosts && ppp->pu->ut_host[0]) { \
  340.             *pb++ = ' '; *pb++ = '('; \
  341.             bufaddn(ppp->pu->ut_host, SZ_UTHOST); \
  342.             *pb++ = ')'; }
  343. #endif    hashosts
  344.  
  345. #ifdef    NOERRMSGS
  346. #define    errexit(code,msg)    exit(code)
  347. #else    NOERRMSGS
  348. #define    errexit(code,msg)    \
  349.         static char errmsg[] = msg; \
  350.         (void) write(2, progname, proglen); \
  351.         (void) write(2, errmsg, sizeof(errmsg) - 1); \
  352.         exit(code)
  353. #endif    NOERRMSGS
  354.         
  355. main(ac, av)
  356. int        ac;
  357. char    *av[];
  358. {
  359.     register struct plus    *pp, *ppp;
  360.     register int            entries;
  361.     register char            *pb, *fr;
  362.     register int            i;
  363.     struct plus                *ppmax;
  364.     struct utmp                *pu, *puu;
  365.     int                        size;
  366.     int                        linelen, entrylen, spaces, cols;
  367.     int                        termord, quick, colreq;
  368.     char                    *linebuf, *aliasbuf, *entryend;
  369.     long                    offset;
  370.     int                        hosts;
  371.  
  372.     proglen = strlen(progname = av[0]);
  373.     requtmpfl = reqaliasfl = (char *) 0;
  374.     hosts = termord = colreq = linelen = quick = 0;
  375.  
  376.     while((i = getopt(ac, av, "hqta:l:c:u:")) != EOF) {
  377.         switch(i) {
  378.         case 'h':    /* show remote host field */
  379.             hosts = 1;
  380.             break;
  381.         case 'a':    /* alias file request */
  382.             reqaliasfl = optarg;
  383.             break;
  384.         case 'u':    /* utmp file request */
  385.             requtmpfl = optarg;
  386.             break;
  387.         case 'q':    /* don't expand */
  388.             quick = 1;
  389.             break;
  390.         case 'l':    /* line length */
  391.             if(*optarg == 't') {
  392.                 linelen = termlen(optarg);
  393.             } else if((linelen = atoi(optarg)) < MINENTLEN + 2) {
  394.                 errexit(1, ": improper line length\n");
  395.             }
  396.             break;
  397.         case 'c':    /* set columns */
  398.             if((colreq = atoi(optarg)) < 1) {
  399.                 errexit(1, ": improper column count\n");
  400.             }
  401.             break;
  402.         case 't':    /* terminal order output */
  403.             termord = 1;
  404.             break;
  405.         case '?':    /* bad option */
  406.         default:    /* error */
  407.             usage();
  408.         }
  409.     }
  410.  
  411.     offset = gmtoffset();
  412.     linelen = linelen ? linelen : DEFLINELEN;
  413.  
  414.     if((i = open(requtmpfl ? requtmpfl : utmpfl, 0)) < 0) {
  415.         errexit(1, ": can't open utmp file\n");
  416.     }
  417.  
  418.     if((entries = (size = fdsize(i))/SZ_UTMP) == 0) {
  419.         /*
  420.         **    no users
  421.         */
  422.         exit(0);
  423.     }
  424.     if(size != entries * SZ_UTMP) {
  425.         errexit(2, ": bad utmp file\n");
  426.     }
  427.  
  428.     puu = pu = (struct utmp *) malloc((unsigned) size);
  429.     ppp = pp = (struct plus *) malloc((unsigned) (entries * SZ_PLUS));
  430.     if(!pu || !pp) {
  431.         errexit(2, ": no memory for utmp entries\n");
  432.     }
  433.     if(read(i, (char *) pu, size) != size) {
  434.         errexit(2, ": read error on utmp file\n");
  435.     }
  436.     (void) close(i);
  437.  
  438.     /*
  439.     **    initialize plus entries, ignoring null utmp entries, then sort
  440.     */
  441.     puu += entries;                        /* start at end */
  442.     entries = 0;                        /* forget... */
  443.     while(puu-- > pu) {                    /* easier to go backwards */
  444.         if(puu->ut_name[0]) {            /* active entry */
  445.             ppp->al = (char *) 0;        /* no alias yet */
  446.             (ppp++)->pu = puu;            /* set utmp pointer */
  447.             ++entries;                    /* active count */
  448.         }
  449.     }
  450.     ppmax = ppp;                        /* at end */
  451.  
  452.     if(entries == 0) {
  453.         /*
  454.         **    there were inactive utmp entries,
  455.         **    but no active entries (yes, it happens)
  456.         */
  457.         exit(0);
  458.     }
  459.  
  460.     if(!quick || !termord) {
  461.         /*
  462.         **    sort by login necessary for alias expansion
  463.         **    default if terminal order not requested
  464.         */
  465.         qsort((char *) pp, entries, SZ_PLUS, logsort);
  466.     }
  467.  
  468.     /*
  469.     **    if alias file, expand aliases
  470.     */
  471.     if(!quick && (aliasbuf = getaliases())) {
  472.         /*
  473.         **    expand aliases for login names
  474.         **    found in the alias file
  475.         */
  476.         expand(pp, ppmax, aliasbuf);
  477.  
  478.         if(termord) {
  479.             /*
  480.             **    put back in terminal order
  481.             */
  482.             qsort((char *) pp, entries, SZ_PLUS, termsort);
  483.         } else {
  484.             /*
  485.             **    sort by name to be printed
  486.             **    (alias if one, login otherwise)
  487.             */
  488.             qsort((char *) pp, entries, SZ_PLUS, alsort);
  489.         }
  490.     }
  491.  
  492.     /*
  493.     **    take extreme pains to format the output
  494.     **        variable number of columns
  495.     **        ragged right (Raytheon standard)
  496.     **        uses as much of line as possible
  497.     **        minimum character output (no tabs)
  498.     **        at least MINSPACES spaces between columns
  499.     **        sorted alphabetically by user name down columns
  500.     **        single write
  501.     */
  502.     entrylen = 0;
  503.     for(ppp = pp;ppp < ppmax;++ppp) {
  504.         if(ppp->al) {
  505.             /*
  506.             **    check alias length
  507.             */
  508.             i = strlen(ppp->al);
  509.         } else {
  510.             /*
  511.             **    check login length
  512.             */
  513.             pb = ppp->pu->ut_name;
  514.             for(i = 0;*pb++ && i < SZ_UTNAME;++i)
  515.                 ;
  516.         }
  517.  
  518. #ifdef    hashosts
  519.         /*
  520.         **    add room for remote host
  521.         */
  522.         if(hosts && ppp->pu->ut_host[0]) {
  523.             size = i + 3;    /* room for parens and space */
  524.             pb = ppp->pu->ut_host;
  525.             for(i = 0;*pb++ && i < SZ_UTHOST;++i)
  526.                 ;
  527.             i += size;
  528.         }
  529. #endif    hashosts
  530.  
  531.         entrylen = (entrylen < i) ? i : entrylen;
  532.     }
  533.  
  534.     if(colreq == 1 || entries == 1) {
  535.         /*
  536.         **    single-column is always valid
  537.         */
  538.         cols = 1;
  539.     } else {
  540.         /*
  541.         **    here's the basic equation for formatting
  542.         **        linelen = (cols - 1) * (entrylen + spaces) + entrylen
  543.         **    solve for columns using
  544.         **        entrylen = maxnamelen + MINENTLEN
  545.         **        spaces = MINSPACES
  546.         **    then solve for entrylen with
  547.         **        spaces = MINSPACES
  548.         **    then solve for spaces
  549.         */
  550.         spaces = MINSPACES;
  551.         entrylen += MINENTLEN;
  552.  
  553.         if(linelen <= entrylen) {
  554.             cols = 1;
  555.         } else {
  556.             cols = 1 + (linelen - entrylen) / (entrylen + spaces);
  557.         }
  558.  
  559.         /*
  560.         **    if there is a column request
  561.         **    check it's validity
  562.         */
  563.         if(colreq && cols > colreq) {
  564.             /*
  565.             **    use number of columns requested 
  566.             */
  567.             cols = colreq;
  568.         }
  569.     }
  570.  
  571.     if(cols == 1) {
  572.         /*
  573.         **    simpler processing for single-column mode
  574.         */
  575.         if(!(pb = linebuf = malloc((unsigned)(entries * entrylen)))) {
  576.             errexit(2, ": no memory for output buffer\n");
  577.         }
  578.  
  579.         for(ppp = pp;ppp < ppmax;++ppp) {
  580.             entryend = pb + linelen;
  581.  
  582.             buftty(ppp->pu->ut_line);
  583.             buftime(ppp->pu->ut_time);
  584.             bufname(ppp);
  585. #ifdef    hashosts
  586.             bufhost(ppp);
  587. #endif    hashosts
  588.  
  589.             /*
  590.             **    truncate if necessary, add newline
  591.             */
  592.             pb = (pb >= entryend) ? (entryend - 1) : pb;
  593.             *pb++ = '\n';
  594.         }
  595.     } else {
  596.         int        lines, linenum, colnum, lastcol, maxcols;
  597.  
  598.         /*
  599.         **    multi-column mode
  600.         **    (can't have more columns than entries)
  601.         */
  602.         cols = (cols > entries) ? entries : cols;
  603.         entrylen = (linelen - (cols - 1) * spaces) / cols;
  604.         spaces = (linelen - cols * entrylen) / (cols - 1);
  605.         lines = entries / cols;
  606.         if(lastcol = entries % cols) {
  607.             ++lines;
  608.         } else {
  609.             lastcol = cols;
  610.         }
  611.  
  612.         if(!(pb = linebuf = malloc((unsigned) (lines * linelen)))) {
  613.             errexit(2, ": no memory for output buffer\n");
  614.         }
  615.  
  616.         maxcols = cols;
  617.         for(linenum = 0;linenum < lines;) {
  618.             ppp = pp + linenum;
  619.             for(colnum = 0;;) {
  620.                 entryend = pb + spaces + entrylen;
  621.  
  622.                 buftty(ppp->pu->ut_line);
  623.                 buftime(ppp->pu->ut_time);
  624.                 bufname(ppp);
  625. #ifdef    hashosts
  626.                 bufhost(ppp);
  627. #endif    hashosts
  628.  
  629.                 if(++colnum < maxcols) {
  630.                     ppp += (colnum <= lastcol) ? lines : (lines - 1);
  631.                     while(pb < entryend) {
  632.                         *pb++ = ' ';
  633.                     }
  634.                     continue;
  635.                 }
  636.                 break;
  637.             }
  638.  
  639.             *pb++ = '\n';
  640.             if(++linenum == lines - 1) {
  641.                 maxcols = lastcol;
  642.             }
  643.         }
  644.     }
  645.  
  646.     size = (int) (pb - linebuf);
  647.     if(write(1, linebuf, size) != size) {
  648.         errexit(2, ": output write error\n");
  649.     }
  650.  
  651.     exit(0);
  652. }
  653.  
  654. /*
  655. **    logsort: sort plus entries by login first, time second
  656. */
  657. logsort(pp0, pp1)
  658. struct plus       *pp0;
  659. struct plus       *pp1;
  660. {
  661.     int            val;
  662.  
  663.     /*
  664.     **    string compare
  665.     */
  666.     if(val = strncmp(pp0->pu->ut_name, pp1->pu->ut_name, SZ_UTNAME)) {
  667.         return(val);
  668.     }
  669.  
  670.     /*
  671.     **    chronological
  672.     */
  673.     return((int) (pp0->pu->ut_time - pp1->pu->ut_time));
  674. }
  675.  
  676. /*
  677. **    alsort: sort plus entries by alias or ut_name first, time second
  678. */
  679. alsort(pp0, pp1)
  680. struct plus       *pp0;
  681. struct plus       *pp1;
  682. {
  683.     int            val;
  684.  
  685.     /*
  686.     **    string compare
  687.     */
  688.     if(pp0->al && pp1->al) {
  689.         /*
  690.         **    both have aliases -- easy
  691.         */
  692.         if(val = strcmp(pp0->al, pp1->al)) {
  693.             return(val);
  694.         }
  695.     } else {
  696.         /*
  697.         **    one or both missing alias
  698.         */
  699.         if(val = strncmp(
  700.             (pp0->al ? pp0->al : pp0->pu->ut_name),            /* arg0 */
  701.             (pp1->al ? pp1->al : pp1->pu->ut_name),            /* arg1 */
  702.             SZ_UTNAME)
  703.         ) {
  704.             return(val);
  705.         }
  706.  
  707.         /*
  708.         **    different lengths?
  709.         **    assume one with alias is longer
  710.         **    (fast but possibly incorrect?)
  711.         */
  712.         if(val = (int) (pp0->al - pp1->al)) {
  713.             return(val);
  714.         }
  715.     }
  716.  
  717.     /*
  718.     **    chronological
  719.     */
  720.     return((int) (pp0->pu->ut_time - pp1->pu->ut_time));
  721. }
  722.  
  723. /*
  724. **    termsort: sort plus entries by terminal order
  725. */
  726. termsort(pp0, pp1)
  727. struct plus       *pp0;
  728. struct plus       *pp1;
  729. {
  730.     return((int)(pp0->pu - pp1->pu));
  731. }
  732.  
  733. /*
  734. **    getaliases: read alias file into dynamic buffer
  735. */
  736. char *
  737. getaliases()
  738. {
  739.     char       *s;
  740.     char       *b;
  741.     int            fd;
  742.     int            size;
  743.  
  744.     if(reqaliasfl) {
  745.         /*
  746.         **    open requested alias file -- must be readable
  747.         */
  748.         if((fd = open(reqaliasfl, 0)) < 0) {
  749.             errexit(1, ": can't open alias file\n");
  750.         }
  751.     } else {
  752.         /*
  753.         **    try to open default alias file
  754.         */
  755.         if(!(b = getenv("HOME"))) {
  756.             return((char *) 0);
  757.         }
  758.  
  759.         if(!(s = malloc(
  760.             (unsigned) ((size = strlen(b)) + 1 + sizeof(aliasfl))))
  761.             ) {
  762.             errexit(2, ": no memory for path name expansion\n");
  763.         }
  764.  
  765.         (void) strcpy(s, b);
  766.         *(s+size++) = '/';
  767.         (void) strcpy(s+size, aliasfl);
  768.  
  769.         if((fd = open(s, 0)) < 0) {
  770.             /*
  771.             **    can't open for read -- not error
  772.             */
  773.             free(s);
  774.             return((char *) 0);
  775.         }
  776.         free(s);
  777.     }
  778.  
  779.     if((size = fdsize(fd)) < 4) {
  780.         /*
  781.         **    smaller than smallest possible alias -- ignore
  782.         */
  783.         (void) close(fd);
  784.         return((char *) 0);
  785.     }
  786.  
  787.     /*
  788.     **    get null-terminated buffer
  789.     */
  790.     if(!(b = malloc((unsigned) (size + 1)))) {
  791.         errexit(2, ": no memory for alias buffer\n");
  792.     }
  793.     *(b+size) = '\0';
  794.  
  795.     if(read(fd, b, size) != size) {
  796.         errexit(2, ": read error on alias file\n");
  797.     }
  798.     (void) close(fd);
  799.  
  800.     return(b);
  801. }
  802.  
  803. /*
  804. **    fdsize: return size of file corresponding to fd
  805. */
  806. fdsize(fd)
  807. int        fd;
  808. {
  809.     struct stat        st;
  810.  
  811.     if(fstat(fd, &st) < 0) {
  812.         errexit(2, ": fstat failed in fdsize\n");
  813.     }
  814.  
  815.     return((int) st.st_size);
  816. }
  817.  
  818. /*
  819. **    expand: expand alias names
  820. */
  821. expand(ppp, ppmax, buf)
  822. register struct plus    *ppp;
  823. register struct plus    *ppmax;
  824. register char            *buf;
  825. {
  826.     register int    next;
  827.     register int    cmp;
  828.     char            *login;
  829.     char            *alias;
  830.  
  831.     next = 1;
  832.     while(ppp < ppmax) {
  833.         if(next) {
  834.             if(!(buf = logalias(buf, &login, &alias))) {
  835.                 /*
  836.                 **    no more list entries
  837.                 */
  838.                 return;
  839.             }
  840.         }
  841.  
  842.         if((cmp = strncmp(login, ppp->pu->ut_name, SZ_UTNAME)) < 0) {
  843.             /*
  844.             **    no match, keep utmp entry, continue in list
  845.             */
  846.             next = 1;
  847.             continue;
  848.         }
  849.  
  850.         if(cmp > 0) {
  851.             /*
  852.             **    no match, next utmp entry, too far in list
  853.             */
  854.             ++ppp;
  855.             next = 0;
  856.             continue;
  857.         }
  858.  
  859.         /*
  860.         **    matched, next utmp entry, try to match this list entry again
  861.         */
  862.         (ppp++)->al = alias;
  863.         next = 0;
  864.     }
  865. }
  866.  
  867. /*
  868. **    logalias: get pointer to login and alias
  869. **        return NULL if end of buffer, new position otherwise
  870. **        buf must point to first character in line
  871. */
  872. char *
  873. logalias(buf, plogin, palias)
  874. register char    *buf;
  875. char            **plogin;
  876. char            **palias;
  877. {
  878.     register int    inword;
  879.  
  880.     inword = 0;
  881.     while(!inword) {                    /* find beginning of login */
  882.         switch(*buf) {
  883.         case '\r':                        /* empty lines -- OK */
  884.         case '\n':                        /* empty lines -- OK */
  885.         WHITESPACE:                        /* white space at beginning */
  886.             ++buf;
  887.             break;
  888.         case '\0':                        /* end of buffer */
  889.             return((char *) 0);
  890.         default:                        /* found login */
  891.             inword = 1;
  892.             *plogin = buf;                /* save login */
  893.             break;
  894.         }
  895.     }
  896.  
  897.     while(inword) {                        /* find end of login */
  898.         switch(*buf) {
  899.         WHITESPACE:                        /* end of login */
  900.             inword = 0;
  901.             *buf++ = '\0';                /* null terminate */
  902.             break;
  903.         case '\0':                        /* end of buffer */
  904.             return((char *) 0);
  905.         default:                        /* still in login */
  906.             ++buf;
  907.             break;
  908.         }
  909.     }
  910.  
  911.     while(!inword) {                    /* find beginning of alias */
  912.         switch(*buf) {
  913.         WHITESPACE:                        /* still in white space */
  914.             ++buf;
  915.             break;
  916.         case '\0':                        /* end of buffer */
  917.             return((char *) 0);
  918.         default:                        /* beginning of alias */
  919.             inword = 1;
  920.             *palias = buf;                /* save alias */
  921.             break;
  922.         }
  923.     }
  924.  
  925.     while(inword) {                        /* find end of alias */
  926.         switch(*buf) {
  927.         case '\r':                        /* end of line */
  928.         case '\n':                        /* end of line */
  929.             inword = 0;                    /* end of alias */
  930.             *buf++ = '\0';                /* null terminate */
  931.             break;
  932.         case '\0':                        /* end of buffer */
  933.             inword = 0;                    /* end of alias */
  934.             break;                        /* leave buffer here (OK) */
  935.         default:                        /* still in alias */
  936.             if(*buf < ' ' || *buf > '~') {    /* ASCII */
  937.                 /*
  938.                 **    replace control characters
  939.                 **    (they screw up output format)
  940.                 */
  941.                 *buf = ' ';                /* replace with space */
  942.             }
  943.             ++buf;
  944.             break;
  945.         }
  946.     }
  947.  
  948.     return(buf);                        /* return buffer position */
  949. }
  950.  
  951. /*
  952. **    usage: print a usage message and exit
  953. */
  954. usage()
  955. {
  956.     static char    preamble[]        = "usage -- ";
  957.     static char    postamble[]        =
  958.         " [-q] [-t] [-c columns] [-l length] [-a aliasfl]\n";
  959.  
  960. #ifdef    hashosts
  961.     static char    hostamble[]    = " [-h]";
  962. #endif    hashosts
  963.  
  964.     (void) write(2, preamble, sizeof(preamble) - 1);
  965.     (void) write(2, progname, proglen);
  966. #ifdef    hashosts
  967.     (void) write(2, hostamble, sizeof(hostamble) - 1);
  968. #endif    hashosts
  969.     (void) write(2, postamble, sizeof(postamble) - 1);
  970.  
  971.     exit(1);
  972. }
  973.  
  974. /*
  975. **    gmtoffset: get seconds offset from gmt
  976. */
  977. long
  978. gmtoffset()
  979. {
  980. #ifdef    bsd42
  981.  
  982.     struct timeval        tv;
  983.     struct timezone        tz;
  984.     struct tm           *pt;
  985.     struct tm           *localtime();
  986.  
  987.     (void) gettimeofday(&tv, &tz);
  988.     pt = localtime((time_t *) &tv.tv_sec);
  989.  
  990.     return(tz.tz_minuteswest * 60 - (pt->tm_isdst ? 3600 : 0));
  991.  
  992. #else    bsd42
  993.  
  994.     struct timeb        tb;
  995.     struct tm           *pt;
  996.     struct tm           *localtime();
  997.  
  998.     (void) ftime(&tb);
  999.     pt = localtime(&tb.time);
  1000.  
  1001.     return((long) tb.timezone * 60 - (pt->tm_isdst ? 3600 : 0));
  1002.  
  1003. #endif    bsd42
  1004. }
  1005.  
  1006. /*
  1007. **    timefmt: format the time string
  1008. **        this routine is ASCII dependent
  1009. */
  1010. char *
  1011. timefmt(tim)
  1012. long    tim;
  1013. {
  1014.     static char    tbf[8];
  1015.     int            t;
  1016.     int            tens;
  1017.  
  1018.     (void) strcpy(tbf, " 00:00 ");            /* initialize */
  1019.  
  1020.     if((t = (tim / 3600) % 24) > 9) {        /* hours */
  1021.         tbf[1] = (tens = t / 10) + '0';
  1022.         tbf[2] = t - (tens * 10) + '0';        /* mult faster than mod */
  1023.     } else {
  1024.         tbf[1] = ' ';                        /* leading space */
  1025.         tbf[2] = t + '0';
  1026.     }
  1027.  
  1028.     if((t = (tim / 60) % 60) > 9) {            /* minutes */
  1029.         tbf[4] = (tens = t / 10) + '0';
  1030.         tbf[5] = t - (tens * 10) + '0';        /* mult faster than mod */
  1031.     } else {
  1032.         tbf[4] = '0';                        /* leading zero */
  1033.         tbf[5] = t + '0';
  1034.     }
  1035.  
  1036.     return(tbf);
  1037. }
  1038.  
  1039. /*
  1040. **    termlen: get line length from termcap
  1041. **        if arg contains '=' use following as
  1042. **        terminal name, else use $TERM
  1043. */
  1044. termlen(arg)
  1045. char   *arg;
  1046. {
  1047.     char   *name;
  1048.     char    tbuf[1024];
  1049.     int        len;
  1050.  
  1051.     if((name = index(arg, '=')) == (char *)0) {
  1052.         name = getenv("TERM");
  1053.     } else {
  1054.         ++name;
  1055.     }
  1056.  
  1057.     if(name != (char *)0) {
  1058.         if(tgetent(tbuf, name) == 1) {
  1059.             if((len = tgetnum("co")) > 0) {
  1060.                 return(tgetflag("am") ? len - 1 : len);        /* avoid wrap */
  1061.             }
  1062.         }
  1063.     }
  1064.  
  1065.     return(DEFLINELEN);
  1066. }
  1067. SHAR_EOF
  1068. if test 17037 -ne "`wc -c < 'nwho.c'`"
  1069. then
  1070.     echo shar: error transmitting "'nwho.c'" '(should have been 17037 characters)'
  1071. fi
  1072. fi
  1073. exit 0
  1074. #    End of shell archive
  1075.